	//
	//  CacheConfig.swift
	//  hello
	//
	//  Created by 赵康 on 2024/11/22.
	//

import Redis
import Foundation

struct CacheConfig {
		// MARK: - 缓存时间配置
	enum Duration {
    static let ultraShort: TimeAmount = .minutes(1)
		static let short: TimeAmount = .minutes(10)
		static let medium: TimeAmount = .hours(1)
		static let long: TimeAmount = .hours(24)
		static let veryLong: TimeAmount = .hours(24 * 7)
		enum Statistics {
			static let daily: TimeAmount = .hours(24)      // 日统计缓存24小时
			static let monthly: TimeAmount = .hours(24*7)  // 月统计缓存一周
			static let ranking: TimeAmount = .hours(6)     // 排行榜缓存6小时
		}
	}
	
		// MARK: - 缓存类型
	enum CacheType {
		case search // 搜索结果
		case album // 专辑
		case audio // 音频
		case user // 用户
		case transcript // 字幕
		case statistics // 统计
    case note // 字幕
    case noteTag
    case noteReview
    case userSettings
    case stat
    case playlist // 播放列表
	}
	
		// MARK: - 缓存键前缀
	enum Prefix: String {
		case version = "v1" // 版本号, 用于批量更新缓存
		case App = "bayanarabic_server_v2" // 应用名
		
		case album = "album"
		case audio = "audio"
		case search = "search"
		case user = "user"
		case transcript = "transcript"
		case statistics = "statistics"
		case ranking = "ranking"
    case note = "note"
    case noteTag
    case noteReview
    case playlist = "playlist"
		
			// 生成带有版本号和应用名的完整前缀
		static func make(_ prefix: Prefix) -> String {
			"\(App):\(version):\(prefix.rawValue)"
		}
	}
		// MARK: - 缓存键生成器
	struct KeyBuilder {
			// AlbumController
    static func album(albumID: UUID, userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.album),
        "album:\(albumID)",
        "user:\(userID)"
      ].joined(separator: ":"))
    }
    
    // 添加公共专辑分享页面的缓存键
    static func albumPublic(albumID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.album),
        "public:\(albumID)"
      ].joined(separator: ":"))
    }
    
    // 添加公共字幕分享页面的缓存键
    static func publicTranscriptsByAudio(audioID: UUID, page: Int, per: Int, total: Int) -> RedisKey {
      RedisKey([
        Prefix.make(.transcript),
        "public:\(audioID)",
        "page:\(page)",
        "per:\(per)",
        "total:\(total)"
      ].joined(separator: ":"))
    }
    
    // 添加公共音频详情的缓存键
    static func publicAudioDetails(audioID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.audio),
        "public:\(audioID)"
      ].joined(separator: ":"))
    }
    
		static func albumsAll(query: AlbumController.FetchQuery, userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.album),
				"all",
				"user\(userID)",
				"page\(query.page)",
				"per\(query.per)"
			].joined(separator: ":"))
		}
    
		static func albumsByCategory(category: String, query: AlbumController.FetchQuery, userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.album),
				"category:\(category)",
				"user\(userID)",
				"page\(query.page)",
				"per\(query.per)"
			].joined(separator: ":"))
		}
    
    static func tags() -> RedisKey {
      RedisKey([
        Prefix.make(.album),
        "tags",
        "all"
      ].joined(separator: ":"))
    }
    
		static func albumsByTag(tagID: UUID, query: AlbumController.TagQuery, userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.album),
				"tag:\(tagID)",
				"user\(userID)",
				"page\(query.page ?? 1)",
				"per\(min(query.per ?? 20, 100))"
			].joined(separator: ":"))
		}
    
    static func search(query: AlbumController.SearchQuery, userID: UUID)-> RedisKey {
      let sanitizedSearchTerm = CacheConfig.sanitizedSearchTerm(query.search)
      let sanitizedTagName = CacheConfig.sanitizedTag(query.tagName ?? "")
      return RedisKey([
        Prefix.make(.search),
        "term:\(sanitizedSearchTerm)",
        "user\(userID)",
        "scope:\(query.effectiveSearchScope)",
        "field:\(String(describing: query.searchField))", // 添加这行
        "category:\(query.category?.rawValue ?? "all")",
        "tagName:\(sanitizedTagName)",
        "sortBy:\(query.sortBy.rawValue)",
        "order:\(query.order.rawValue)",
        "page:\(query.page)",
        "per:\(query.per)"
      ].joined(separator: ":"))
    }

			// FavoriteController
		static func favorites(userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.album),
				"favorites",
				"user\(userID)"
			].joined(separator: ":"))
		}
			// AudioController
		static func audiosByAlbum(albumID: UUID, userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.audio),
				"album:\(albumID)",
				"user\(userID)"
			].joined(separator: ":"))
		}
    
		static func audioHistories(audioIds: [UUID], userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.audio),
				"histories",
				"user\(userID)",
				"audioIds\(audioIds.map { $0.uuidString }.joined(separator: "-"))"
			].joined(separator: ":"))
		}
			// TranscriptController
		static func transcriptsByAudio(audioID: UUID, page: Int, per: Int, total: Int, userID: UUID) -> RedisKey {
			RedisKey([
				Prefix.make(.transcript),
				"audio:\(audioID)",
				"user\(userID)",
				"page:\(page)",
				"per:\(per)",
				"total:\(total)"
			].joined(separator: ":"))
		}
			// 统计相关健的生成
		static func dailyStatistics(userID: UUID, date: Date, tagID: UUID? = nil) -> RedisKey {
				// 使用 Calendar.mondayFirst 确保日期的一致性
			let dayStart = Calendar.mondayFirst.startOfDay(for: date)
			let dateString = DateFormatter.yyyyMMdd.string(from: dayStart)
			
			let components = [
				Prefix.make(.statistics),
				"daily",
				"user:\(userID)",
				"date:\(dateString)",
				tagID.map { "tag:\($0)" }
			].compactMap { $0 }
			
			return RedisKey(components.joined(separator: ":"))
		}
		static func yearlyStatistics(
			userID: UUID,
			tagID: UUID?
		) -> RedisKey {
			let now = Date()
			let todayStart = Calendar.mondayFirst.startOfDay(for: now)
			let year = Calendar.mondayFirst.component(.year, from: todayStart)
			
			let components = [
				Prefix.make(.statistics),
				"yearly",
				"user:\(userID)",
				"year:\(year)",
				tagID.map { "tag:\($0)" }
			].compactMap { $0 }
			
			return RedisKey(components.joined(separator: ":"))
		}
    
		static func monthlyStatistics(
			userID: UUID,
			month: Date,
			tagID: UUID?
		) -> RedisKey {
				// 确保使用月份的开始时间
			let monthStart = Calendar.mondayFirst.startOfMonth(for: month)
			let monthString = DateFormatter.yyyyMM.string(from: monthStart)
			
			let components = [
				Prefix.make(.statistics),
				"monthly",
				"user:\(userID)",
				"month:\(monthString)",
				tagID.map { "tag:\($0)" }
			].compactMap { $0 }
			
			return RedisKey(components.joined(separator: ":"))
		}
    
		static func userRanking(timeRange: TimeRange, tagID: UUID?) -> RedisKey {
			let components = [
				Prefix.make(.ranking),
				"userRanking",
				"timeRange:\(timeRange.redisKey)",
				tagID.map { "tag:\($0)" }
			].compactMap { $0 }
			
			return RedisKey(components.joined(separator: ":"))
		}
    
    static func notesByAudio(audioID: UUID, userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.note),
        "audio\(audioID)",
        "user\(userID)",
      ].joined(separator: ":"))
    }
    
    static func noteSearch(query: String, userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.note),
        "search",
        "user:\(userID)",
        "query:\(query.lowercased())",
      ].joined(separator: ":"))
    }
    
    static func allNotesByAudio(audioID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.note),
        "all",
        "audio:\(audioID)",
      ].joined(separator: ":"))
    }
    
    static func notesByUser(userID: UUID) -> RedisKey {
      RedisKey ([
        Prefix.make(.note),
        "user:\(userID)",
      ].joined(separator: ":"))
    }
    
    static func notesByTag(tag: String, userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.note),
        "tag:\(tag)",
        "user:\(userID)",
      ].joined(separator: ":"))
    }
    
    static func noteTagsByUser(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.noteTag),
        "user:\(userID)"
      ].joined(separator: ":"))
    }
    static func userReviewSettings(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.noteReview),
        "user:\(userID)",
        "review_settings"
      ].joined(separator: ":"))
    }
    static func todayReviews(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.noteReview),
        "user:\(userID)",
        "today_review"
      ].joined(separator: ":"))
    }
    static func reviewStats(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.noteReview),
        "user:\(userID)"
      ].joined(separator: ":"))
    }
    
    static func reviewPrediction(userID: UUID, predictionType: String = "custom_days", customDays: Int? = nil) -> RedisKey {
      let daysParam = customDays != nil ? "days:\(customDays!)" : ""
      
      return RedisKey([
        Prefix.make(.noteReview),
        "user:\(userID)",
        "prediction",
        "type:\(predictionType)",
        daysParam
      ].filter { !$0.isEmpty }.joined(separator: ":"))
    }
    
    // 播放列表相关
    static func playlist(playlistID: UUID, userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.playlist),
        "id:\(playlistID)",
        "user:\(userID)"
      ].joined(separator: ":"))
    }
    
    static func playlistsForUser(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.playlist),
        "all",
        "user:\(userID)"
      ].joined(separator: ":"))
    }
    
    // 添加最近7天音频数量的缓存键
    static func recentAudiosCount(userID: UUID) -> RedisKey {
      RedisKey ([
        Prefix.make(.statistics),
        "recent:7days",
        "user:\(userID)",
        "audio:count"
      ].joined(separator: ":"))
    }
    
    // 添加所有用户最近7天音频数量分布的缓存键
    static func allUsersRecentAudiosDistribution() -> RedisKey {
      RedisKey ([
        Prefix.make(.statistics),
        "recent:7days",
        "all:users",
        "audio:distribution"
      ].joined(separator: ":"))
    }
    
    // 添加用户标签分布的缓存键
    static func tagsDistribution(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.statistics),
        "recent:7days",
        "user:\(userID)",
        "tags:distribution"
      ].joined(separator: ":"))
    }
    
    // 收藏夹相关的键
    static func folders(userID: UUID) -> RedisKey {
      RedisKey([
        Prefix.make(.album),
        "folders",
        "user\(userID)"
      ].joined(separator: ":"))
    }
    
    static func albumsInFolder(userID: UUID, folderID: UUID, sortOrder: FavoritesSortOrder? = nil) -> RedisKey {
      RedisKey([
        Prefix.make(.album),
        "folder",
        "user\(userID)",
        "folder\(folderID)",
        sortOrder.map { "sort:\($0.rawValue)" } ?? "sort:default"
      ].joined(separator: ":"))
    }
 	}
  
	static func sanitizedSearchTerm(_ term: String) -> String {
		term
			.lowercased()
			.trimmingCharacters(in: .whitespacesAndNewlines)
			.replacingOccurrences(of: ":", with: "_")
	}
  
	static func sanitizedTag(_ tag: String) -> String {
		tag
			.lowercased()
			.trimmingCharacters(in: .whitespacesAndNewlines)
			.replacingOccurrences(of: ":", with: "_")
	}
}

struct Cache {
	let logger: Logger
	
		// 获取缓存
	func get<T: Codable>(
		forKey key: RedisKey,
		as type: T.Type,
		from redis: RedisClient
	) async throws -> T? {
		guard let cacheData = try await redis.get(key, asJSON: String.self)
		else {
			return nil
		}
		do {
			return try JSONDecoder().decode(T.self, from: Data(cacheData.utf8))
		} catch {
			logger.error("缓存解析失败: \(error)")
			return nil
		}
	}
		// 设置缓存
	func set<T: Codable>(
		_ value: T,
		forKey key: RedisKey,
		duration: TimeAmount,
		type: CacheConfig.CacheType,
		on redis: RedisClient
	) async throws {
		do {
			let jsonData = try JSONEncoder().encode(value)
			guard let jsonString = String(data: jsonData, encoding: .utf8)
			else {
				logger.error("缓存编码失败")
				return
			}
			try await redis.set(key, toJSON: jsonString)
			let _ = redis.expire(key, after: duration)
		} catch {
			logger.error("缓存设置失败: \(error)")
		}
	}
		// 删除缓存
	func delete(forKey key: RedisKey, on redis: RedisClient) async throws {
		let _ = redis.delete(key)
	}
		// 批量删除缓存
    // 添加到 CacheConfig.swift 中的 Cache 结构体
  func batchDelete(forPattern pattern: String, on redis: RedisClient) async throws {
      // 使用 Redis SCAN 命令查找匹配的键
    var cursor = 0
    
    repeat {
        // 将 EventLoopFuture 转换为 async/await
      let scanResult = try await withCheckedThrowingContinuation { continuation in
        redis.scan(
          startingFrom: cursor,
          matching: pattern,
          count: 100
        ).whenComplete { result in
          continuation.resume(with: result)
        }
      }
      
      cursor = scanResult.0
      let keys = scanResult.1.map { RedisKey($0) }
      
        // 批量删除找到的键
      if !keys.isEmpty {
        let deletedCount = try await withCheckedThrowingContinuation { continuation in
          redis.delete(keys).whenComplete { result in
            continuation.resume(with: result)
          }
        }
        logger.debug("批量删除缓存键: \(deletedCount) 个")
      }
    } while cursor != 0
  }
}
